"""
支付工具类：分布式锁、订单号生成、签名验证等
"""
import hashlib
import hmac
import time
import uuid
from datetime import datetime
from typing import Optional
from contextlib import contextmanager
import time as time_module

from utils.redis_utils import get_redis
from loguru import logger


class PaymentUtils:
    """支付工具类"""
    
    @staticmethod
    def generate_order_no(user_id: int) -> str:
        """
        生成唯一订单号
        格式：日期(YYYYMMDD) + 随机数(8位) + 用户ID后4位
        """
        date_str = datetime.now().strftime('%Y%m%d')
        random_str = uuid.uuid4().hex[:8].upper()
        user_suffix = str(user_id).zfill(4)[-4:]
        return f"ORD{date_str}{random_str}{user_suffix}"
    
    @staticmethod
    def generate_payment_no(order_no: str) -> str:
        """生成支付流水号"""
        timestamp = int(time_module.time() * 1000)
        random_str = uuid.uuid4().hex[:6].upper()
        return f"PAY{timestamp}{random_str}"
    
    @staticmethod
    def generate_refund_no(order_no: str) -> str:
        """生成退款流水号"""
        timestamp = int(time_module.time() * 1000)
        random_str = uuid.uuid4().hex[:6].upper()
        return f"REF{timestamp}{random_str}"
    
    @staticmethod
    def amount_to_cents(amount: float) -> int:
        """金额转换为分（避免浮点误差）"""
        return int(round(amount * 100))
    
    @staticmethod
    def cents_to_amount(cents: int) -> float:
        """分转换为金额"""
        return round(cents / 100.0, 2)
    
    @staticmethod
    def md5_sign(data: str, key: str) -> str:
        """MD5签名"""
        sign_str = f"{data}{key}"
        return hashlib.md5(sign_str.encode('utf-8')).hexdigest().upper()
    
    @staticmethod
    def sha256_sign(data: str, key: str) -> str:
        """SHA256签名"""
        return hmac.new(
            key.encode('utf-8'),
            data.encode('utf-8'),
            hashlib.sha256
        ).hexdigest()
    
    @staticmethod
    def verify_md5_sign(data: str, sign: str, key: str) -> bool:
        """验证MD5签名"""
        expected_sign = PaymentUtils.md5_sign(data, key)
        return expected_sign.upper() == sign.upper()
    
    @staticmethod
    def verify_sha256_sign(data: str, sign: str, key: str) -> bool:
        """验证SHA256签名"""
        expected_sign = PaymentUtils.sha256_sign(data, key)
        return expected_sign == sign


class DistributedLock:
    """基于Redis的分布式锁"""
    
    def __init__(self, key: str, timeout: int = 30, retry_times: int = 3, retry_delay: float = 0.1):
        """
        初始化分布式锁
        
        Args:
            key: 锁的键名
            timeout: 锁的超时时间（秒）
            retry_times: 重试次数
            retry_delay: 重试延迟（秒）
        """
        self.key = f"lock:{key}"
        self.timeout = timeout
        self.retry_times = retry_times
        self.retry_delay = retry_delay
        self.redis = get_redis()
        self.lock_value = None
    
    def acquire(self) -> bool:
        """获取锁"""
        self.lock_value = str(uuid.uuid4())
        for _ in range(self.retry_times):
            if self.redis.set(self.key, self.lock_value, nx=True, ex=self.timeout):
                logger.debug(f"获取分布式锁成功: {self.key}")
                return True
            time_module.sleep(self.retry_delay)
        logger.warning(f"获取分布式锁失败: {self.key}")
        return False
    
    def release(self) -> bool:
        """释放锁"""
        if not self.lock_value:
            return False
        
        # 使用Lua脚本确保原子性：只有锁的持有者才能释放
        lua_script = """
        if redis.call("get", KEYS[1]) == ARGV[1] then
            return redis.call("del", KEYS[1])
        else
            return 0
        end
        """
        result = self.redis.eval(lua_script, 1, self.key, self.lock_value)
        if result:
            logger.debug(f"释放分布式锁成功: {self.key}")
        else:
            logger.warning(f"释放分布式锁失败: {self.key}，可能锁已过期或被其他进程释放")
        return bool(result)
    
    def __enter__(self):
        if not self.acquire():
            raise RuntimeError(f"无法获取分布式锁: {self.key}")
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.release()


@contextmanager
def distributed_lock(key: str, timeout: int = 30):
    """分布式锁上下文管理器"""
    lock = DistributedLock(key, timeout)
    try:
        if lock.acquire():
            yield lock
        else:
            raise RuntimeError(f"无法获取分布式锁: {key}")
    finally:
        lock.release()

